#include <stdio.h>
#include "esp_err.h"
#include "user_nvs.h"

static const char *TAG = "[NVS]";

#define NAME_SPACE "storage"

nvs_handle_t user_handle;

void user_nvs_init()
{
    esp_err_t err = nvs_flash_init();
    if(err != ESP_OK) ESP_LOGE(TAG, "nvs init error");
    if(err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND){
        ESP_LOGW(TAG, "nvs error(%s), erase...", esp_err_to_name(err));
        ESP_ERROR_CHECK(nvs_flash_erase());
        err = nvs_flash_init();
    }
    ESP_ERROR_CHECK(err);
}

void user_nvs_erase()
{
    ESP_ERROR_CHECK(nvs_flash_erase());
}

bool user_nvs_u16_set(const char* name, uint16_t data)
{
    esp_err_t err = nvs_open(NAME_SPACE, NVS_READWRITE, &user_handle);
    if(err != ESP_OK){
        ESP_LOGE(TAG, "Error (%s) opening NVS handle!", esp_err_to_name(err));
        return false;
    } else{
        err = nvs_set_u16(user_handle, name, data);
        if(err != ESP_OK){
            ESP_LOGE(TAG, "NVS WRITE ERROR: %s", esp_err_to_name(err));
            nvs_close(user_handle);
            return false;
        }
        vTaskDelay(pdMS_TO_TICKS(50));            
        err = nvs_commit(user_handle);
        if(err != ESP_OK){
            ESP_LOGE(TAG, "NVS WRITE COMMIT ERROR: %s", esp_err_to_name(err));
            nvs_close(user_handle);
            return false;
        } 
        vTaskDelay(pdMS_TO_TICKS(50));
        nvs_close(user_handle);
        return true;
    }
}

esp_err_t user_nvs_u16_get(const char* name, uint16_t* data)
{
    esp_err_t err = nvs_open(NAME_SPACE, NVS_READWRITE, &user_handle);
    if(err != ESP_OK){
        ESP_LOGE(TAG, "Error (%s) opening NVS handle!", esp_err_to_name(err));
        return err;
    } else{
        err = nvs_get_u16(user_handle, name, data);
        if(err == ESP_ERR_NVS_NOT_FOUND){
            ESP_LOGW(TAG, "cannnot find key in nvs");
            nvs_close(user_handle);
            return ESP_ERR_NVS_NOT_FOUND;
        }
        if(err != ESP_OK){
            ESP_LOGE(TAG, "Error (%s) read NVS!", esp_err_to_name(err));
            nvs_close(user_handle);
            return err;
        }
    }
    nvs_close(user_handle);
    return err;
}

bool user_nvs_u8_set(const char* name, uint8_t data)
{
    esp_err_t err = nvs_open(NAME_SPACE, NVS_READWRITE, &user_handle);
    if(err != ESP_OK){
        ESP_LOGE(TAG, "Error (%s) opening NVS handle!", esp_err_to_name(err));
        return false;
    } else{
        err = nvs_set_u8(user_handle, name, data);
        if(err != ESP_OK){
            ESP_LOGE(TAG, "NVS WRITE ERROR: %s", esp_err_to_name(err));
            nvs_close(user_handle);
            return false;
        }
        vTaskDelay(pdMS_TO_TICKS(50));            
        err = nvs_commit(user_handle);
        if(err != ESP_OK){
            ESP_LOGE(TAG, "NVS WRITE COMMIT ERROR: %s", esp_err_to_name(err));
            nvs_close(user_handle);
            return false;
        } 
        vTaskDelay(pdMS_TO_TICKS(50));
        nvs_close(user_handle);
        return true;
    }
}

esp_err_t user_nvs_u8_get(const char* name, uint8_t* data)
{
    esp_err_t err = nvs_open(NAME_SPACE, NVS_READWRITE, &user_handle);
    if(err != ESP_OK){
        ESP_LOGE(TAG, "Error (%s) opening NVS handle!", esp_err_to_name(err));
        return err;
    } else{
        err = nvs_get_u8(user_handle, name, data);
        if(err == ESP_ERR_NVS_NOT_FOUND){
            ESP_LOGW(TAG, "cannnot find key in nvs");
            nvs_close(user_handle);
            return ESP_ERR_NVS_NOT_FOUND;
        }
        if(err != ESP_OK){
            ESP_LOGE(TAG, "Error (%s) read NVS!", esp_err_to_name(err));
            nvs_close(user_handle);
            return err;
        }
    }
    nvs_close(user_handle);
    return err;
}

esp_err_t user_nvs_array_set(const char* name, const void* data, size_t len)
{
    esp_err_t err = nvs_open(NAME_SPACE, NVS_READWRITE, &user_handle);
    if(err != ESP_OK){
        ESP_LOGE(TAG, "Error (%s) opening NVS handle!", esp_err_to_name(err));
        return ESP_FAIL;
    } else{
        err = nvs_set_blob(user_handle, name, data, len);
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "Error (%s) saving array data!", esp_err_to_name(err));
            nvs_close(user_handle);
            return err;
        }
        // Commit
        err = nvs_commit(user_handle);
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "Error (%s) committing data!", esp_err_to_name(err));
        }
        nvs_close(user_handle);
        return err;
    }
    return ESP_OK;
}

int user_nvs_array_len_get(const char* name)
{
    esp_err_t err = nvs_open(NAME_SPACE, NVS_READWRITE, &user_handle);
    if(err != ESP_OK){
        ESP_LOGE(TAG, "Error (%s) opening NVS handle!", esp_err_to_name(err));
        return -1;
    } else{
        size_t len = 0;
        err = nvs_get_blob(user_handle, name, NULL, &len);
        if(err == ESP_OK){
            nvs_close(user_handle);
            return len;
        } else{
            ESP_LOGE(TAG, "nvs_array_len_get error: %s", esp_err_to_name(err));
            nvs_close(user_handle);
            return -1;
        }
    }
}

esp_err_t user_nvs_array_get(const char* name, uint8_t* data, size_t len_max)
{
    esp_err_t err = nvs_open(NAME_SPACE, NVS_READWRITE, &user_handle);
    if(err != ESP_OK){
        ESP_LOGE(TAG, "Error (%s) opening NVS handle!", esp_err_to_name(err));
        return err;
    } else{
        size_t get_data_size;
        err = nvs_get_blob(user_handle, name, NULL, &get_data_size);
        if(err == ESP_OK && get_data_size > 0 && get_data_size <= len_max){  // 判断数据长度是否合适
            err = nvs_get_blob(user_handle, name, data, &get_data_size);
            // Commit
            err = nvs_commit(user_handle);
            if (err != ESP_OK) {
                ESP_LOGE(TAG, "Error (%s) committing data!", esp_err_to_name(err));
            }
            nvs_close(user_handle);
            return err;
        }
        nvs_close(user_handle);
        return err; 
    }
}
